Jackson-এর মাধ্যমে JSON ডেটা প্রসেসিং সঠিকভাবে কাজ করছে কিনা তা নিশ্চিত করতে Unit Testing অপরিহার্য। Unit Testing এর জন্য সাধারণত JUnit এবং AssertJ এর মত টুল ব্যবহার করা হয়।
Unit Testing কীভাবে কাজ করে?
- JSON Serialization Validation: Object থেকে JSON স্ট্রিং তৈরি হচ্ছে কিনা তা পরীক্ষা করা।
- JSON Deserialization Validation: JSON স্ট্রিং থেকে Object সঠিকভাবে তৈরি হচ্ছে কিনা তা পরীক্ষা করা।
- Custom Serializer/Deserializer Validation: কাস্টম লজিক সঠিকভাবে কাজ করছে কিনা তা পরীক্ষা করা।
- Edge Case Handling: Null, Empty এবং Invalid JSON এর জন্য পরীক্ষা করা।
প্রয়োজনীয় টুল এবং ডিপেন্ডেন্সি
Maven ডিপেন্ডেন্সি
<dependencies>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- AssertJ -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
</dependencies>
1. JSON Serialization Test
উদাহরণ:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class JacksonSerializationTest {
@Test
public void testSerialization() throws Exception {
// Arrange
User user = new User(1, "John Doe", "john.doe@example.com");
ObjectMapper objectMapper = new ObjectMapper();
// Act
String json = objectMapper.writeValueAsString(user);
// Assert
assertThat(json).contains("\"id\":1");
assertThat(json).contains("\"name\":\"John Doe\"");
assertThat(json).contains("\"email\":\"john.doe@example.com\"");
}
}
// Model Class
class User {
private int id;
private String name;
private String email;
public User(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters and Setters
}
2. JSON Deserialization Test
উদাহরণ:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class JacksonDeserializationTest {
@Test
public void testDeserialization() throws Exception {
// Arrange
String json = "{\"id\":1,\"name\":\"John Doe\",\"email\":\"john.doe@example.com\"}";
ObjectMapper objectMapper = new ObjectMapper();
// Act
User user = objectMapper.readValue(json, User.class);
// Assert
assertThat(user.getId()).isEqualTo(1);
assertThat(user.getName()).isEqualTo("John Doe");
assertThat(user.getEmail()).isEqualTo("john.doe@example.com");
}
}
3. Custom Serializer Test
Custom Serializer:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class CustomUserSerializer extends JsonSerializer<User> {
@Override
public void serialize(User user, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeStringField("userId", String.valueOf(user.getId()));
gen.writeStringField("userName", user.getName());
gen.writeEndObject();
}
}
Test:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class CustomSerializerTest {
@Test
public void testCustomSerializer() throws Exception {
// Arrange
User user = new User(1, "John Doe", "john.doe@example.com");
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(User.class, new CustomUserSerializer());
objectMapper.registerModule(module);
// Act
String json = objectMapper.writeValueAsString(user);
// Assert
assertThat(json).contains("\"userId\":\"1\"");
assertThat(json).contains("\"userName\":\"John Doe\"");
}
}
4. Invalid JSON Test
উদাহরণ:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class InvalidJsonTest {
@Test
public void testInvalidJson() {
// Arrange
String invalidJson = "{ \"id\": 1, \"name\": \"John\", \"email\": 12345 }"; // Invalid email
ObjectMapper objectMapper = new ObjectMapper();
// Act & Assert
assertThatThrownBy(() -> objectMapper.readValue(invalidJson, User.class))
.isInstanceOf(Exception.class)
.hasMessageContaining("Cannot deserialize value of type");
}
}
5. Null Handling Test
উদাহরণ:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class NullHandlingTest {
@Test
public void testNullFields() throws Exception {
// Arrange
User user = new User(1, null, null);
ObjectMapper objectMapper = new ObjectMapper();
// Act
String json = objectMapper.writeValueAsString(user);
// Assert
assertThat(json).contains("\"id\":1");
assertThat(json).doesNotContain("name");
assertThat(json).doesNotContain("email");
}
}
6. Testing with Arrays/Lists
উদাহরণ:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class ArrayDeserializationTest {
@Test
public void testArrayDeserialization() throws Exception {
// Arrange
String json = "[{\"id\":1,\"name\":\"John Doe\",\"email\":\"john.doe@example.com\"},"
+ "{\"id\":2,\"name\":\"Jane Doe\",\"email\":\"jane.doe@example.com\"}]";
ObjectMapper objectMapper = new ObjectMapper();
// Act
List<User> users = objectMapper.readValue(json, new TypeReference<List<User>>() {});
// Assert
assertThat(users).hasSize(2);
assertThat(users.get(0).getName()).isEqualTo("John Doe");
assertThat(users.get(1).getEmail()).isEqualTo("jane.doe@example.com");
}
}
7. Edge Case Test
Null JSON:
@Test
public void testNullJson() {
ObjectMapper objectMapper = new ObjectMapper();
assertThatThrownBy(() -> objectMapper.readValue(null, User.class))
.isInstanceOf(NullPointerException.class);
}
Empty JSON:
@Test
public void testEmptyJson() throws Exception {
String emptyJson = "{}";
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(emptyJson, User.class);
assertThat(user.getId()).isEqualTo(0);
assertThat(user.getName()).isNull();
assertThat(user.getEmail()).isNull();
}
- Jackson Unit Testing নিশ্চিত করে যে JSON ডেটা প্রসেসিং সঠিকভাবে কাজ করছে।
- Serialization, Deserialization, এবং Custom Serializer/Deserializer সঠিকভাবে পরীক্ষা করা যায়।
- Edge Cases এবং Invalid JSON হ্যান্ডল করার জন্য পরীক্ষা গুরুত্বপূর্ণ।
- AssertJ এবং JUnit ব্যবহার করে সহজেই Validation এবং Exception Management করা যায়।
Jackson এর সাথে Unit Testing API-এর নির্ভরযোগ্যতা এবং স্থায়িত্ব নিশ্চিত করে।
Jackson এর জন্য Unit Test তৈরি করতে আমরা সাধারণত JUnit এবং ObjectMapper ব্যবহার করি। Unit Test-এ আমরা Jackson-এর serialization (Java থেকে JSON) এবং deserialization (JSON থেকে Java) প্রক্রিয়াগুলো যাচাই করি।
Unit Test এর ধাপসমূহ
- Dependency Management:
JUnitএবংJacksonএর প্রয়োজনীয় ডিপেন্ডেন্সি যোগ করা। - Test Setup:
ObjectMapperইন্সট্যান্স তৈরি করা। - Serialization Test: Java Object থেকে JSON-এ সঠিক রূপান্তর নিশ্চিত করা।
- Deserialization Test: JSON থেকে Java Object-এ সঠিক রূপান্তর নিশ্চিত করা।
- Edge Case Handling: অপ্রত্যাশিত বা ভুল ডেটার জন্য টেস্টিং।
Maven Dependencies
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
Java Model Class
import com.fasterxml.jackson.annotation.JsonProperty;
public class User {
@JsonProperty("user_name")
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Getters এবং Setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && name.equals(user.name);
}
}
Unit Test Code
১. Serialization Test
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class UserSerializationTest {
@Test
public void testSerialization() throws Exception {
// Arrange
User user = new User("John Doe", 25);
ObjectMapper objectMapper = new ObjectMapper();
// Act
String json = objectMapper.writeValueAsString(user);
// Assert
String expectedJson = "{\"user_name\":\"John Doe\",\"age\":25}";
assertEquals(expectedJson, json);
}
}
২. Deserialization Test
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class UserDeserializationTest {
@Test
public void testDeserialization() throws Exception {
// Arrange
String json = "{\"user_name\":\"John Doe\",\"age\":25}";
ObjectMapper objectMapper = new ObjectMapper();
// Act
User user = objectMapper.readValue(json, User.class);
// Assert
User expectedUser = new User("John Doe", 25);
assertEquals(expectedUser, user);
}
}
৩. Edge Case Test
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class UserEdgeCaseTest {
@Test
public void testInvalidJson() {
// Arrange
String invalidJson = "{\"user_name\":\"John Doe\",\"age\":\"invalid\"}";
ObjectMapper objectMapper = new ObjectMapper();
// Act and Assert
assertThrows(Exception.class, () -> objectMapper.readValue(invalidJson, User.class));
}
@Test
public void testMissingField() throws Exception {
// Arrange
String partialJson = "{\"user_name\":\"John Doe\"}";
ObjectMapper objectMapper = new ObjectMapper();
// Act
User user = objectMapper.readValue(partialJson, User.class);
// Assert
User expectedUser = new User("John Doe", 0); // Default age is 0
assertEquals(expectedUser, user);
}
}
৪. Custom Serializer/Deserializer Test
Custom Serializer
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class CustomUserSerializer extends JsonSerializer<User> {
@Override
public void serialize(User user, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeStringField("full_name", user.getName().toUpperCase());
gen.writeNumberField("user_age", user.getAge());
gen.writeEndObject();
}
}
Custom Serializer Test
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CustomSerializerTest {
@Test
public void testCustomSerializer() throws Exception {
// Arrange
User user = new User("John Doe", 25);
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(User.class, new CustomUserSerializer());
objectMapper.registerModule(module);
// Act
String json = objectMapper.writeValueAsString(user);
// Assert
String expectedJson = "{\"full_name\":\"JOHN DOE\",\"user_age\":25}";
assertEquals(expectedJson, json);
}
}
পরীক্ষার কাভারেজ
- Serialization: Java Object → JSON।
- Deserialization: JSON → Java Object।
- Edge Cases: যেমন ভুল ডেটা টাইপ, মিসিং ফিল্ড।
- Custom Serializer/Deserializer।
- Exception Handling।
- JUnit: Jackson-এর serialization এবং deserialization প্রক্রিয়া যাচাই করার জন্য সহজ এবং কার্যকর।
- Edge Cases: ভুল JSON ডেটা এবং মিসম্যাচ ফিল্ডের জন্য পরীক্ষা করুন।
- Custom Logic: কাস্টম Serializer/Deserializer এর জন্য টেস্ট রাইটিং অত্যন্ত গুরুত্বপূর্ণ।
- Automation: Unit Test কোড Jackson-এর সাথে কাজ করার সময় সঠিকতা নিশ্চিত করে এবং বাগ কমায়।
Jackson ব্যবহার করে JSON ডেটা Serialize/Deserialize করার জন্য JUnit এবং Mockito দিয়ে টেস্টিং করা একটি সাধারণ পদ্ধতি। এই পদ্ধতিতে, JSON ডেটা হ্যান্ডলিং এবং API এর বেহেভিয়ার নিশ্চিত করতে ObjectMapper এবং মক করা সেবাগুলোর ওপর নির্ভর করে টেস্ট তৈরি করা হয়।
প্রয়োজনীয় Maven ডিপেনডেন্সি
<dependencies>
<!-- JUnit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>
Step-by-Step Integration উদাহরণ
১. মডেল ক্লাস তৈরি করা
public class User {
private String name;
private int age;
private String email;
// Constructors, Getters, and Setters
public User() {}
public User(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
২. JUnit দিয়ে Jackson টেস্ট করা
ObjectMapper ব্যবহার করে Serialization/Deserialization টেস্ট
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class JacksonTest {
@Test
public void testSerialization() throws Exception {
// Arrange
User user = new User("Alice", 30, "alice@example.com");
ObjectMapper mapper = new ObjectMapper();
// Act
String json = mapper.writeValueAsString(user);
// Assert
String expectedJson = "{\"name\":\"Alice\",\"age\":30,\"email\":\"alice@example.com\"}";
assertEquals(expectedJson, json);
}
@Test
public void testDeserialization() throws Exception {
// Arrange
String json = "{\"name\":\"Bob\",\"age\":25,\"email\":\"bob@example.com\"}";
ObjectMapper mapper = new ObjectMapper();
// Act
User user = mapper.readValue(json, User.class);
// Assert
assertEquals("Bob", user.getName());
assertEquals(25, user.getAge());
assertEquals("bob@example.com", user.getEmail());
}
}
৩. Mockito ব্যবহার করে Service Layer টেস্ট করা
Service Layer
import org.springframework.stereotype.Service;
@Service
public class UserService {
public String getUserData(User user) {
if (user.getAge() < 18) {
return "User is a minor";
}
return "User is an adult";
}
}
Mockito দিয়ে Service Layer টেস্ট
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
public class UserServiceTest {
@Test
public void testGetUserData() {
// Arrange
UserService userService = Mockito.mock(UserService.class);
User user = new User("Charlie", 17, "charlie@example.com");
// Mocking the service method
when(userService.getUserData(user)).thenReturn("User is a minor");
// Act
String result = userService.getUserData(user);
// Assert
assertEquals("User is a minor", result);
}
}
৪. Spring Boot এবং Controller টেস্ট করা
Controller Layer
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public String createUser(@RequestBody User user) {
return "User created: " + user.getName();
}
}
MockMvc ব্যবহার করে Controller টেস্ট
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
public void testCreateUser() throws Exception {
// Arrange
User user = new User("Alice", 30, "alice@example.com");
String json = objectMapper.writeValueAsString(user);
// Act & Assert
mockMvc.perform(post("/api/users")
.contentType("application/json")
.content(json))
.andExpect(status().isOk())
.andExpect(content().string("User created: Alice"));
}
}
৫. Test Coverage নিশ্চিত করা
- Unit Test: মডেল ক্লাস এবং সার্ভিস লেয়ার টেস্ট করার জন্য JUnit ব্যবহার।
- Mocking External Services: Mockito দিয়ে ডিপেন্ডেন্সি মক করা।
- Integration Test: MockMvc দিয়ে Controller এবং API টেস্ট করা।
- Serialization/Deserialization: Jackson এর ObjectMapper ব্যবহার করে নিশ্চিত করা যে JSON ফরম্যাট সঠিক।
- JUnit: JSON Serialization/Deserialization ভেরিফাই করতে ব্যবহার করা হয়।
- Mockito: ডিপেন্ডেন্সি মক করে সার্ভিস লেয়ার বা ডেটাবেস ইন্টারঅ্যাকশন টেস্ট করা হয়।
- Spring Boot Test Framework: MockMvc দিয়ে REST API টেস্ট করা।
- ObjectMapper: Jackson এর মাধ্যমে JSON ডেটার গুণগত মান নিশ্চিত করতে ব্যবহৃত হয়।
JSON Serialization এবং Deserialization Jackson-এর অন্যতম গুরুত্বপূর্ণ ফিচার। API বা ডেটা প্রসেসিং অ্যাপ্লিকেশনে ডেটা সঠিকভাবে হ্যান্ডল করার জন্য Serialization এবং Deserialization প্রক্রিয়ার সঠিকতা যাচাই করা অত্যন্ত গুরুত্বপূর্ণ। Test Coverage নিশ্চিত করার জন্য JUnit এবং Mocking Framework (যেমন Mockito) ব্যবহার করা হয়।
Serialization এবং Deserialization কী?
- Serialization: Java Object কে JSON String-এ রূপান্তর করা।
- Deserialization: JSON String কে Java Object-এ রূপান্তর করা।
Serialization এবং Deserialization Test Coverage কেন গুরুত্বপূর্ণ?
- ডেটা সঠিকভাবে JSON ফরম্যাটে রূপান্তর হচ্ছে কিনা তা যাচাই করা।
- JSON থেকে Java Object সঠিকভাবে তৈরি হচ্ছে কিনা তা নিশ্চিত করা।
- Missing Fields, Unknown Fields, এবং Invalid Data Handling নিশ্চিত করা।
- Custom Serializer/Deserializer ঠিকমতো কাজ করছে কিনা তা যাচাই করা।
1. Serialization Test Coverage
Serialization Test Case Example:
মডেল ক্লাস:
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL) // Exclude null fields during serialization
public class User {
public int id;
public String name;
public String email;
public User(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
}
Serialization টেস্ট কোড:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class SerializationTest {
@Test
public void testSerialization() throws Exception {
User user = new User(101, "John Doe", null); // Null email should be excluded
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
// Validate JSON string
assertTrue(json.contains("\"id\":101"));
assertTrue(json.contains("\"name\":\"John Doe\""));
assertFalse(json.contains("email")); // Email is null, so it should not be present
}
}
আউটপুট:
Serialization Passed: JSON format is valid and excludes null fields.
2. Deserialization Test Coverage
Deserialization Test Case Example:
JSON String Example:
{
"id": 101,
"name": "John Doe",
"email": "john.doe@example.com"
}
Deserialization টেস্ট কোড:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class DeserializationTest {
@Test
public void testDeserialization() throws Exception {
String json = """
{
"id": 101,
"name": "John Doe",
"email": "john.doe@example.com"
}
""";
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(json, User.class);
// Validate Java Object
assertEquals(101, user.id);
assertEquals("John Doe", user.name);
assertEquals("john.doe@example.com", user.email);
}
}
আউটপুট:
Deserialization Passed: JSON fields correctly mapped to Java object.
3. Handling Missing and Unknown Fields
Test for Missing Fields:
@Test
public void testMissingFields() throws Exception {
String json = """
{
"id": 101,
"name": "John Doe"
}
"""; // "email" is missing
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(json, User.class);
// Validate Java Object
assertEquals(101, user.id);
assertEquals("John Doe", user.name);
assertNull(user.email); // Email should be null
}
Test for Unknown Fields:
@Test
public void testUnknownFields() throws Exception {
String json = """
{
"id": 101,
"name": "John Doe",
"unknownField": "extra data"
}
"""; // Unknown field "unknownField"
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // Ignore unknown fields
User user = objectMapper.readValue(json, User.class);
// Validate Java Object
assertEquals(101, user.id);
assertEquals("John Doe", user.name);
}
4. Testing Custom Serializer and Deserializer
Custom Serializer:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class CustomNameSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString("Name: " + value);
}
}
Serializer Test:
@Test
public void testCustomSerializer() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(User.class, CustomSerializerMixin.class);
User user = new User(101, "John Doe", null);
String json = objectMapper.writeValueAsString(user);
// Validate custom serialization
assertTrue(json.contains("\"name\":\"Name: John Doe\""));
}
Mixin Class:
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public abstract class CustomSerializerMixin {
@JsonSerialize(using = CustomNameSerializer.class)
public String name;
}
5. Testing Error Scenarios
Invalid Data:
@Test
public void testInvalidData() {
String invalidJson = """
{
"id": "invalid",
"name": "John Doe"
}
""";
ObjectMapper objectMapper = new ObjectMapper();
assertThrows(JsonMappingException.class, () -> {
objectMapper.readValue(invalidJson, User.class);
});
}
Null or Empty JSON:
@Test
public void testEmptyJson() throws Exception {
String emptyJson = "{}";
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(emptyJson, User.class);
// Validate default values
assertEquals(0, user.id); // Default int value
assertNull(user.name); // Null value
}
Best Practices for Test Coverage
- Positive Cases:
- Serialization এবং Deserialization-এর জন্য সঠিক JSON এবং Object ব্যবহার করুন।
- Negative Cases:
- Invalid JSON, Missing Fields, এবং Unknown Fields টেস্ট করুন।
- Boundary Conditions:
- Null, Empty JSON, এবং Complex JSON হ্যান্ডল করুন।
- Custom Logic Testing:
- Custom Serializer এবং Deserializer সঠিকভাবে কাজ করছে কিনা তা যাচাই করুন।
- Error Handling:
- Exception Management সঠিকভাবে কাজ করছে কিনা তা নিশ্চিত করুন।
উপকারিতা
- বাগ শনাক্তকরণ: Serialization এবং Deserialization সমস্যা দ্রুত শনাক্ত।
- Reliable Code: API বা ডেটা প্রসেসিং সিস্টেম আরও নির্ভরযোগ্য।
- Custom Logic Validation: Custom Serializer এবং Deserializer কার্যকারিতা নিশ্চিত।
এই পদ্ধতিগুলো অনুসরণ করে আপনি Jackson Serialization এবং Deserialization-এর জন্য শক্তিশালী Test Coverage তৈরি করতে পারবেন।
Jackson ব্যবহার করে Complex Object-এর serialization, deserialization, validation এবং Unit Testing অত্যন্ত গুরুত্বপূর্ণ, বিশেষ করে RESTful API ডেভেলপমেন্টে। সঠিক টেস্টিং এবং ভ্যালিডেশন নিশ্চিত করলে ডেটার সঠিকতা এবং অ্যাপ্লিকেশনের নির্ভরযোগ্যতা বজায় থাকে।
Unit Testing এবং Validation এর ধাপসমূহ
- Setup for Unit Testing:
- JUnit বা TestNG টেস্ট ফ্রেমওয়ার্ক ব্যবহার করুন।
- Jackson-এর ObjectMapper ব্যবহার করে serialization/deserialization টেস্ট করুন।
- Data Validation:
- Java Bean Validation (JSR 380) এর মাধ্যমে ডেটা যাচাই করুন।
- কাস্টম validation এবং exception handling অন্তর্ভুক্ত করুন।
- Testing Complex Objects:
- Nested objects এবং collections এর জন্য টেস্ট কেস তৈরি করুন।
- JSON schema validation অন্তর্ভুক্ত করুন।
1. Maven Dependencies
JUnit এবং Jackson-এর জন্য নিম্নলিখিত ডিপেনডেন্সি যুক্ত করুন:
<dependencies>
<!-- JUnit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- Hibernate Validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
</dependencies>
2. Complex Object Model তৈরি করুন
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.util.List;
public class Order {
@NotNull(message = "Order ID cannot be null")
private String orderId;
@Valid
@NotNull(message = "Customer cannot be null")
private Customer customer;
@Valid
@NotNull(message = "Products cannot be null")
private List<Product> products;
// Getters and Setters
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
public List<Product> getProducts() { return products; }
public void setProducts(List<Product> products) { this.products = products; }
}
class Customer {
@NotNull(message = "Customer Name cannot be null")
@Size(min = 2, max = 50, message = "Customer Name must be between 2 and 50 characters")
private String name;
@NotNull(message = "Email cannot be null")
private String email;
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
class Product {
@NotNull(message = "Product Name cannot be null")
private String name;
@NotNull(message = "Price cannot be null")
private Double price;
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Double getPrice() { return price; }
public void setPrice(Double price) { this.price = price; }
}
3. Unit Test Cases for Serialization and Deserialization
Serialization Test:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
class SerializationTest {
@Test
void testSerialization() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Product product1 = new Product();
product1.setName("Laptop");
product1.setPrice(1200.50);
Product product2 = new Product();
product2.setName("Mouse");
product2.setPrice(25.75);
Customer customer = new Customer();
customer.setName("Alice");
customer.setEmail("alice@example.com");
Order order = new Order();
order.setOrderId("ORD123");
order.setCustomer(customer);
order.setProducts(Arrays.asList(product1, product2));
String json = mapper.writeValueAsString(order);
System.out.println("Serialized JSON: " + json);
// Assert JSON contains key fields
assertEquals(true, json.contains("ORD123"));
assertEquals(true, json.contains("Laptop"));
assertEquals(true, json.contains("Alice"));
}
}
Deserialization Test:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class DeserializationTest {
@Test
void testDeserialization() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = """
{
"orderId": "ORD123",
"customer": {
"name": "Alice",
"email": "alice@example.com"
},
"products": [
{
"name": "Laptop",
"price": 1200.50
},
{
"name": "Mouse",
"price": 25.75
}
]
}
""";
Order order = mapper.readValue(json, Order.class);
// Assertions
assertNotNull(order);
assertNotNull(order.getCustomer());
assertEquals(2, order.getProducts().size());
}
}
4. Validation Test Cases
Validation Logic:
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertFalse;
class ValidationTest {
private final Validator validator;
public ValidationTest() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
this.validator = factory.getValidator();
}
@Test
void testValidationErrors() {
Product product = new Product();
product.setName("Laptop"); // Missing price
Customer customer = new Customer();
customer.setName("Alice");
customer.setEmail(null); // Invalid email
Order order = new Order();
order.setOrderId("ORD123");
order.setCustomer(customer);
order.setProducts(Arrays.asList(product));
Set<jakarta.validation.ConstraintViolation<Order>> violations = validator.validate(order);
// Assert that validation errors exist
assertFalse(violations.isEmpty());
violations.forEach(violation -> System.out.println(violation.getMessage()));
}
}
আউটপুট:
Price cannot be null
Email cannot be null
5. Exception Handling During Testing
Example of Handling Missing Fields:
@Test
void testMissingFields() {
ObjectMapper mapper = new ObjectMapper();
String invalidJson = """
{
"orderId": "ORD123",
"products": []
}
""";
Exception exception = null;
try {
mapper.readValue(invalidJson, Order.class);
} catch (Exception e) {
exception = e;
}
assertNotNull(exception);
System.out.println("Exception: " + exception.getMessage());
}
6. Schema Validation for Complex Objects
JSON Schema Validation:
@Test
void testJsonSchemaValidation() throws Exception {
String schema = """
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"orderId": {"type": "string"},
"customer": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"}
},
"required": ["name", "email"]
},
"products": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"}
},
"required": ["name", "price"]
}
}
},
"required": ["orderId", "customer", "products"]
}
""";
String jsonData = """
{
"orderId": "ORD123",
"customer": {
"name": "Alice",
"email": "alice@example.com"
},
"products": [
{"name": "Laptop", "price": 1200.50}
]
}
""";
ObjectMapper mapper = new ObjectMapper();
JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
JsonSchema jsonSchema = factory.getJsonSchema(mapper.readTree(schema));
JsonNode data = mapper.readTree(jsonData);
ProcessingReport report = jsonSchema.validate(data);
assertTrue(report.isSuccess(), "Validation failed: " + report);
}
- Serialization/Deserialization Testing: Complex objects-এর জন্য নিশ্চিত
করুন JSON সঠিকভাবে প্রক্রিয়াজাত হচ্ছে।
2. Validation Testing: Nested objects এবং collections এর জন্য Bean Validation ব্যবহার করুন।
3. Error Handling: Exception Logging এবং Detailed Messages দিয়ে debugging সহজ করুন।
4. Schema Validation: JSON Schema দিয়ে JSON গঠন যাচাই করুন।
এই ধাপগুলো অনুসরণ করে আপনার Complex Object-এর জন্য নির্ভরযোগ্য এবং কার্যকর Unit Testing এবং Validation নিশ্চিত করা সম্ভব।
Read more